
#ifndef _SEPARATED_BODIES_
#define _SEPARATED_BODIES_

//--------------------------------------------------------------------------------------
// This file contains implementation of bodies in OOP-style: each body is defined as
// independent object and has incapsulated parameters (position and mass). This version
// of bodies is user-friendly but ineffective due to ignoring of available memory-access
// optimizations.
//--------------------------------------------------------------------------------------

#include <vector>
#include <ttg/math.h>
#include "InteractionLogic.h"

using namespace ttg::math;

struct SeparatedBody
{
	public:
		float mass;
		vec3f position;
		vec3f velocity;
		vec3f acceleration;
	public:
		//Creates body with default parameters.
		inline SeparatedBody()
		{ mass = 0.0f; }
};

//Set of OOP-styled bodies.
class SeparatedBodies
{
	public:
		std::vector<SeparatedBody> bodies;
	public:
		//Resizes bodies.
		TTG_INLINE void resize(size_t new_size)
		{ bodies.resize(new_size); }
		//Returns count of bodies.
		TTG_INLINE size_t getSize()
		{ return bodies.size(); }
	public:
		//SubVector of bodies. Will be used for unified access.
		struct SubVector
		{
			private:
				SeparatedBodies *owner;
				size_t lo, hi;
				int tag;								//user tag.
			public:
				//Creates empty vector.
				TTG_INLINE SubVector()
				{
					this->owner = NULL;
					this->lo = 0;
					this->hi = 0;
					this->tag = 0;
				}
				//Creates vector with all bodies.
				TTG_INLINE SubVector(SeparatedBodies *owner)
				{
					this->owner = owner;
					this->lo = 0;
					this->hi = owner->bodies.size();
					this->tag = 0;
				}
				//Creates vector with required range of bodies.
				TTG_INLINE SubVector(SubVector &sv, size_t lo, size_t hi)
				{
					this->owner = sv.owner;
					this->lo = sv.lo + lo;
					this->hi = sv.lo + hi;
					this->tag = 0;
				}
				//Returns tag, that was assigned by user.
				TTG_INLINE int getTag()
				{ return tag; }
				//Sets user's tag.
				TTG_INLINE void setTag(int tag)
				{ this->tag = tag; }
				//Returns real index of n-th element.
				TTG_INLINE size_t getRealIndex(size_t n)
				{ return n + lo; }
				//Returns size of current vector.
				TTG_INLINE size_t getSize() const
				{ return hi - lo; }
				//Returns position of i-th body.
				TTG_INLINE vec3f getPosition(size_t i)
				{
					return owner->bodies[getRealIndex(i)].position;
				}
				//Returns owner of this sub-vector.
				TTG_INLINE SeparatedBodies *getOwner()
				{ return owner; }
				//Returns SubVector, that wraps all bodies.
				TTG_INLINE SubVector getOwnerSubVector()
				{ return SubVector(getOwner()); }
				//Interacts two bodies ("b1" with "b2") of this subvector.
				//Updates velocity of b1, but does not update "b2".
				TTG_INLINE void interactWith(size_t b1, size_t b2)
				{
					//Getting real indices.
					size_t rb1 = getRealIndex(b1);
					size_t rb2 = getRealIndex(b2);
					if (rb1 == rb2)
						return;
					//Interacting.
					InteractionLogic::interactWith(//first body
												   owner->bodies[rb1].position.x,
												   owner->bodies[rb1].position.y,
												   owner->bodies[rb1].position.z,
												   owner->bodies[rb1].acceleration.x,
												   owner->bodies[rb1].acceleration.y,
												   owner->bodies[rb1].acceleration.z,
												   owner->bodies[rb1].mass,
												   //second body
												   owner->bodies[rb2].position.x,
												   owner->bodies[rb2].position.y,
												   owner->bodies[rb2].position.z,
												   owner->bodies[rb2].mass);
				}
				//Interacts two bodies ("b1" with "b2") of diffrent subvectors.
				//Updates velocity of b1, but does not update "b2".
				TTG_INLINE void interactWith(size_t b1, SubVector &sv2, size_t b2)
				{
					//Getting real indices.
					size_t rb1 = getRealIndex(b1);
					size_t rb2 = sv2.getRealIndex(b2);
					if (getOwner() == sv2.getOwner() && rb1 == rb2)
						return;
					//Interacting.
					InteractionLogic::interactWith(//first body
												   owner->bodies[rb1].position.x,
												   owner->bodies[rb1].position.y,
												   owner->bodies[rb1].position.z,
												   owner->bodies[rb1].acceleration.x,
												   owner->bodies[rb1].acceleration.y,
												   owner->bodies[rb1].acceleration.z,
												   owner->bodies[rb1].mass,
												   //second body
												   sv2.getOwner()->bodies[rb2].position.x,
												   sv2.getOwner()->bodies[rb2].position.y,
												   sv2.getOwner()->bodies[rb2].position.z,
												   sv2.getOwner()->bodies[rb2].mass);
				}
				//Interacts requred bodies.
				//Updates accelerations of "b1" and "b2"
				TTG_INLINE void interactWithEachOther(size_t b1, size_t b2)
				{
					//Getting real indices.
					size_t rb1 = getRealIndex(b1);
					size_t rb2 = getRealIndex(b2);
					if (rb1 == rb2)
						return;
					//Interacting.
					InteractionLogic::interactWithEachOther(
												   //first body
												   owner->bodies[rb1].position.x,
												   owner->bodies[rb1].position.y,
												   owner->bodies[rb1].position.z,
												   owner->bodies[rb1].acceleration.x,
												   owner->bodies[rb1].acceleration.y,
												   owner->bodies[rb1].acceleration.z,
												   owner->bodies[rb1].mass,
												   //second body
												   owner->bodies[rb2].position.x,
												   owner->bodies[rb2].position.y,
												   owner->bodies[rb2].position.z,
												   owner->bodies[rb2].acceleration.x,
												   owner->bodies[rb2].acceleration.y,
												   owner->bodies[rb2].acceleration.z,
												   owner->bodies[rb2].mass);
				}
				//Interacts requred bodies of two subvectors.
				//Updates accelerations of "b1" and "b2"
				TTG_INLINE void interactWithEachOther(size_t b1, SubVector &sv2, size_t b2)
				{
					//Getting real indices.
					size_t rb1 = getRealIndex(b1);
					size_t rb2 = getRealIndex(b2);
					if (getOwner() == sv2.getOwner() && rb1 == rb2)
						return;
					//Interacting.
					InteractionLogic::interactWithEachOther(
												   //first body
												   owner->bodies[rb1].position.x,
												   owner->bodies[rb1].position.y,
												   owner->bodies[rb1].position.z,
												   owner->bodies[rb1].acceleration.x,
												   owner->bodies[rb1].acceleration.y,
												   owner->bodies[rb1].acceleration.z,
												   owner->bodies[rb1].mass,
												   //second body
												   sv2.getOwner()->bodies[rb2].position.x,
												   sv2.getOwner()->bodies[rb2].position.y,
												   sv2.getOwner()->bodies[rb2].position.z,
												   sv2.getOwner()->bodies[rb2].acceleration.x,
												   sv2.getOwner()->bodies[rb2].acceleration.y,
												   sv2.getOwner()->bodies[rb2].acceleration.z,
												   sv2.getOwner()->bodies[rb2].mass);
				}
				//Updates position and velocity of required body
				//and zeroes its acceleration.
				TTG_INLINE void update(size_t b, float dt)
				{
					size_t rb = getRealIndex(b);
					InteractionLogic::update(owner->bodies[rb].position.x,
											 owner->bodies[rb].position.y,
											 owner->bodies[rb].position.z,
											 owner->bodies[rb].velocity.x,
											 owner->bodies[rb].velocity.y,
											 owner->bodies[rb].velocity.z,
											 owner->bodies[rb].acceleration.x,
											 owner->bodies[rb].acceleration.y,
											 owner->bodies[rb].acceleration.z,
											 dt);
				}
		};
};

#endif
